#include <queue>
#include <map>
#include <set>
#include <gflags/gflags.h>
#include <glog/logging.h>

#include "core/grape.h"
#include "state.hpp"

using namespace std;

template<class VD, class ED, class MessageT, class ResultT, class TemporalT>
class VF2Worker: public Worker<VD, ED, MessageT, ResultT, TemporalT> {
	void pEval(Fragment<VD, ED> &fragment,
	           MessageBuffer<MessageT> &messageBuffer,
	           ResultT &partialResult,
	           TemporalT &temporal) {

		// set<map<int, int>> matches;
		set<int> matches;

		this->query.buildSignature();

		VLOG(2) << this->query.printPattern() << std::endl;

		vf2_inspector<VD, ED> inspector(this->query, fragment);


		// matches = inspector.findEnumVF2MatchesMultiThreads(0, *fragment.getInnerVertices(), 4);
		matches = inspector.findDesignedVF2MatchesMultiThreads(0, *fragment.getInnerVertices(), 4);

		// int cnt = 0;
		// int skip  = 0;
		// for (auto x : innerVertices) {
		// 	cnt++;
		// 	if (cnt % 10000 == 0) {
		// 		cerr << "frag-" << fragment.getFragmentID() << " prcessed " << cnt << "/" << fragment.getNumVertices() << "\n";
		// 	}
		// 	try {

		// 		if (!match(this->pattern.getSig(OUT_R1, 0), fragment.getSig(OUT_R1, x))
		// 			||!match(this->pattern.getSig(IN_R1, 0), fragment.getSig(IN_R1, x))
		// 			||!match(this->pattern.getSig(OUT_R2, 0), fragment.getSig(OUT_R2, x))
		// 			||!match(this->pattern.getSig(IN_R2, 0), fragment.getSig(IN_R2, x))) {
		// 			//sig not match
		// 			skip ++;
		// 			continue;
		// 		}

		// 		set<map<int, int> > temp = inspector.findEnumVF2Matches(0, x);
		// 		if (temp.size() != 0) {
		// 			matches.insert(temp.begin(), temp.end());
		// 		}
		// 	} catch (...) {
		// 		cerr << "vertex " << x << " in fragment " << fragment.getFragmentID() << " should be verify.";
		// 	}
		// }
		// cerr<<"skip = "<<skip<<"\n";
		VLOG(1) << "partialMatches.size = " << matches.size() << "\n";
		partialResult = matches;
	}

	void incEval(Fragment<VD, ED> &fragment,
	             MessageBuffer<MessageT> &messageBuffer,
	             ResultT &partialResult,
	             TemporalT &temporal) {
	}

	void assemble(vector<ResultT> &partialResults) {

		set<int> finalResult;
		for (auto pr : partialResults) {
			finalResult.insert(pr.begin(), pr.end());
		}

		if (this->result_file != "") {
			std::ofstream fout;
			fout.open(this->result_file);
			LOG(INFO) << "total match size = " << finalResult.size() << "\n";


			for (auto v : finalResult) {
				fout << v << "\n";
			}


			// for (set<map<int, int> >::iterator it = finalResult.begin(); it != finalResult.end(); it++) {
			// 	fout << "[";
			// 	for (int i = 0; i < (*it).size(); i++) {
			// 		if (i != 0) fout << ", ";
			// 		fout << i << "->" << (*it).at(i);
			// 	}
			// 	fout << "]" << "\n";
			// }

			fout.close();
		}
	}
};

int main(int argc, char *argv[]) {

	FLAGS_stderrthreshold = 0;

	google::SetUsageMessage("Usage: mpiexec.hydra [mpi_opts] ./vf2 [grape_opts]");
	google::ParseCommandLineFlags(&argc, &argv, true);

	init_workers();

	WorkerParams param;

	param.algorithm = "vf2";
	param.enable_assemble = true;
	param.use_pattern = true;
	param.graph_load_strategy = REPLICATED;
	param.index_strategy = SIGNATURE_HOP_2;

	google::ShutDownCommandLineFlags();

	// VF2Worker<int, int, int, set<map<int, int> >, int> vf2Worker;
	VF2Worker<int, int, int, set<int>, int> vf2Worker;

	google::InitGoogleLogging("vf2");
	vf2Worker.run(param);
	google::ShutdownGoogleLogging();
	worker_finalize();
	return 0;

}
